package com.quanyue.haircut.order.service.impl;

import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.util.SignUtils;
import com.quanyue.auto.util.AssertUtils;
import com.quanyue.auto.util.TimeUtils;
import com.quanyue.auto.wechat.PayType;
import com.quanyue.auto.wechat.handler.WechatContext;
import com.quanyue.auto.wechat.service.DomainApplyService;
import com.quanyue.auto.wechat.service.WechatPayService;
import com.quanyue.haircut.domain.constant.OrderType;
import com.quanyue.haircut.domain.constant.PayResult;
import com.quanyue.haircut.domain.entity.CardOrder;
import com.quanyue.haircut.domain.entity.ReserveOrder;
import com.quanyue.haircut.domain.entity.User;
import com.quanyue.haircut.order.service.CardOrderService;
import com.quanyue.haircut.order.service.PayService;
import com.quanyue.haircut.order.service.ReserveOrderService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Service
public class PayServiceImpl implements PayService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private WxPayService wxPayService;
    @Autowired
    private ReserveOrderService reserveOrderService;
    @Autowired
    private CardOrderService cardOrderService;
    @Autowired
    private DomainApplyService domainApplyService;
    @Autowired
    private WechatPayService wechatPayService;

    @Override
    public Map<String, String> pay(String orderType, Integer merchantNo, BigDecimal price, String orderNo, User user, String userIp) {
        Map<String, String> result = new HashMap<>();
        if (price.compareTo(BigDecimal.ZERO) != 0) {
            BigDecimal amount = price.multiply(BigDecimal.valueOf(100));
            result = wechatPay(orderType, merchantNo, "订单号" + orderNo + "-支付", orderNo + amount.intValue(), amount, user, userIp);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handlePayConfirm(WxPayOrderNotifyResult result) {
        try {
            this.handleOrder(result);
        } catch (WxPayException e) {
            logger.error("WxError", e);
        }
    }

    private void handleOrder(WxPayOrderNotifyResult result) throws WxPayException {
        String orderNo = result.getOutTradeNo();
        if (orderNo.length() > 20) {
            orderNo = orderNo.substring(0, 20);
        }
        Integer merchantNo = null;
        Object order = null;
        if (result.getAttach().equals(OrderType.RESERVE.name())) {
            order = reserveOrderService.findOrderByNo(orderNo);
            merchantNo = ((ReserveOrder) order).getMerchantNo();
        } else {
            order = cardOrderService.findOrderByNo(orderNo);
            merchantNo = ((CardOrder) order).getMerchantNo();
        }
        AssertUtils.notNull(order, "订单不存在");
        WxPayOrderQueryResult orderResult;
        WechatContext.setMerchantNo(merchantNo);
        orderResult = wxPayService.queryOrder(result.getTransactionId(), "");
        if (orderResult.getReturnCode().equals("SUCCESS")
                && orderResult.getResultCode().equals("SUCCESS")) {
            if (result.getAttach().equals(OrderType.RESERVE.name())) {
                reserveOrderService.afterPayComplete(orderNo, orderResult.getTransactionId(), PayResult.valueOf(orderResult.getTradeState()));
            } else {
                cardOrderService.afterPayComplete(orderNo, orderResult.getTransactionId(), PayResult.valueOf(orderResult.getTradeState()));
            }
        }

    }

    private Map<String, String> wechatPay(String orderType, Integer merchantNo, String body, String tradeNo, BigDecimal leftFee, User user, String ip) {
        WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
        orderRequest.setBody(body);
        orderRequest.setOutTradeNo(tradeNo);
        orderRequest.setTotalFee(leftFee.intValue());
        orderRequest.setOpenid(user.getOpenId());
        orderRequest.setSpbillCreateIp(ip);
        // 附加数据 用来区分场景支付
        orderRequest.setAttach(orderType);
        LocalDateTime now = LocalDateTime.now();
        orderRequest.setTimeStart(TimeUtils.toDateTimeString(now));
        orderRequest.setTimeExpire(TimeUtils.toDateTimeString(now.plusMinutes(14)));
        orderRequest.setNotifyURL(domainApplyService.findApprovedDomain(merchantNo) + "/wechat/pay/confirm");
        try {
            WechatContext.setPayType(PayType.PAY);
            WechatContext.setMerchantNo(merchantNo);
            WxPayUnifiedOrderResult result = wxPayService.unifiedOrder(orderRequest);
            String prepayId = result.getPrepayId();
            if (StringUtils.isBlank(prepayId)) {
                throw new RuntimeException(String.format("无法获取prepay id，错误代码： '%s'，信息：%s。",
                        result.getErrCode(), result.getErrCodeDes()));
            }

            Map<String, String> payInfo = new HashMap<>();
            payInfo.put("appId", result.getAppid());
            // 支付签名时间戳，注意微信jssdk中的所有使用timestamp字段均为小写。
            // 但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
            payInfo.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
            payInfo.put("nonceStr", String.valueOf(System.currentTimeMillis()));
            payInfo.put("package", "prepay_id=" + prepayId);
            payInfo.put("signType", "MD5");
            if ("NATIVE".equals(orderRequest.getTradeType())) {
                payInfo.put("codeUrl", result.getCodeURL());
            }
            payInfo.put("paySign", SignUtils.createSign(payInfo, "", wechatPayService.findByMerchantNo(merchantNo).getMchKey(), false));
            return payInfo;
        } catch (WxPayException e) {
            logger.error("支付失败", e);
            throw new AssertUtils.ValidateException(e.getMessage());
        }
    }
}
